package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.cookie.BestMatchSpecFactory;
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.exception.self.PageErrorResultException;
import com.foreveross.crawl.common.exception.self.UnableParseRouteTypeException;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.Maps;

/**
 * 俄罗斯航空，  要传递 JSESSIONID 才能拿到数据
 * @author ljshi
 *
 */
public class AeroflotAdapter extends AbstractAdapter {
	
	private HttpClientContext context=null;
	
	/**
	 * 舱位
	 */
	private final static Map<String, String> CABIN = new HashMap<String, String>(){ 
		{
			put("PE", "ECONOMY");
			put("BE", "ECONOMY");
			put("VE", "ECONOMY");
			put("PC", "PREMIUM_ECONOMY");
			put("VB", "BUSINESS");
			put("PB", "BUSINESS");
		}
	};
			
	
			
	public AeroflotAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}
	

	@Override
	public Object fetch(String arg0) throws Exception {
		switch (super.getRouteType()) {
		case INTERNATIONAL_ROUND:
			return fetchInterRound();
		default:
			throw new UnableParseRouteTypeException(taskQueue, getRouteType().getName());
		}
	}

	@Override
	public String getUrl() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		if(fetchObject==null){
			throw new Exception("获取数据为空");
		}
		JSONObject j=(JSONObject) fetchObject;
		switch (super.getRouteType()) {
		case INTERNATIONAL_ROUND:
			return paraseInterRound(j);
		default:
			throw new UnableParseRouteTypeException(taskQueue, getRouteType().getName());
		}
	}

	@Override
	public boolean validateFetch(Object obj) throws Exception {
		if (obj == null) {
			throw new Exception("获取数据为空");
		}
		return true;
	}
	
	private Object fetchInterRound() throws Exception {
		//按一个舱位查询可以查询出所有的舱位
		return fetchPageDatas("ECONOMY");
	}


	private Object fetchPageDatas(String cabinType) throws Exception {
		
		String page =fetchStep(cabinType); // 单个舱位的
		if("Not FlightNo".equalsIgnoreCase(page))
			throw new FlightInfoNotFoundException("无该航班");
		JSONObject j=JSONObject.fromObject(page.trim().replaceAll("^\\(|\\)$", ""));
		return j;
	}
	
	/**
	 * 解析数据
	 * @param j
	 * @return
	 * @throws Exception 
	 */
	private List<Object> paraseInterRound(JSONObject j) throws Exception {
		JSONObject model;
		JSONArray goWay;//去程
		JSONArray returnWay;//返程
		List<DoublePlaneInfoEntity> result=new ArrayList<DoublePlaneInfoEntity>();
		boolean hasFilght=false;
		try {
			JSONArray content=j.getJSONArray("content");
			model=content.getJSONObject(0).getJSONObject("model");
			goWay=model.getJSONArray("outbounds");
			returnWay=model.getJSONArray("inbounds");
			if(null!=goWay && null!=returnWay)
				hasFilght=true;
			//解析去程
			result=this.paraseList(goWay);
			Set<ReturnDoublePlaneInfoEntity> returnPlaneInfos =this.parasereturnWay(returnWay);
			List<ReturnDoublePlaneInfoEntity> listReturn = new ArrayList<ReturnDoublePlaneInfoEntity>(returnPlaneInfos);
			PlaneInfoEntityBuilder.buildRelation(result, listReturn);
			for(DoublePlaneInfoEntity plane:result){
				plane.setReturnPlaneInfos(returnPlaneInfos);
			}
		} finally {
			j=null;
			model=null;
			goWay=null;
			returnWay=null;
		}
		try{
		PlaneInfoEntityBuilder.buildLimitPrice(result);
		}catch(Exception e){
			logger.info("国际往返排序错误，请检查数据");
			logger.info(e);
			try{
				logger.info(result);
			}catch(Exception e1){
				logger.info("打印数据出错");
			}
		}
		if(result.size()==0 && !hasFilght)
			throw new FlightInfoNotFoundException("你选择的日期没有航班信息,请重新选择!");
		else if(result.size()==0 && hasFilght)
			throw new Exception("解析错误");
		return Arrays.asList(result.toArray());
	}
	
	/**
	 * 解析回程
	 * @param returnWay
	 * @return
	 */
	private Set<ReturnDoublePlaneInfoEntity> parasereturnWay(JSONArray returnWay) {
		Set<ReturnDoublePlaneInfoEntity> returnPlaneInfos = new HashSet<ReturnDoublePlaneInfoEntity>();
		for(Object obj:returnWay){
			try {
				JSONObject flight=(JSONObject) obj;
				JSONObject trip=flight.getJSONObject("itineraryPartData");
				JSONArray flightNumber=trip.getJSONArray("flightNumber");//航班号 2314
				JSONArray departureCodes=trip.getJSONArray("departureCodes");//出发城市三字码
				JSONArray arrivalCodes=trip.getJSONArray("arrivalCodes");//到达城市三字码
				JSONArray aircraftType=trip.getJSONArray("aircraftType");//飞机类型
				JSONArray classOfService= trip.getJSONArray("classOfService");//舱位名称
				JSONArray operatingCarrier=trip.getJSONArray("operatingCarrier");//简称  //SU
				JSONArray airlineCodes=trip.getJSONArray("airlineCodes");//航空公司简称
				//TODO 运营商抓不到…… 只能抓到简称
				ReturnDoublePlaneInfoEntity planeInfo=PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, airlineCodes.getString(0),
						"Aeroflot-Russian Airlines", "Aeroflot-Russian Airlines",
						null, null, 
						operatingCarrier.getString(0)+flightNumber.getString(0),
						null, null, null, aircraftType.getString(0), null, null, null, null,
						ReturnDoublePlaneInfoEntity.class);
				planeInfo.setFlightDuration(trip.getLong("totalTripDuration"));//飞行总时长
				planeInfo.setStartTime(this.parse(trip.getString("departureDate"),"yyyy/MM/dd HH:mm:ss"));
				planeInfo.setEndTime(this.parse(trip.getString("arrivalDate"),"yyyy/MM/dd HH:mm:ss"));
				planeInfo.setCurrency("CNY");
				
				//中转集合
				if(flightNumber.size()>1){
					JSONArray segments= flight.getJSONArray("segments"); //中转
					ArrayList<ReturnTransitEntity> transits = new ArrayList<ReturnTransitEntity>();
					for(int i=0;i<flightNumber.size();i++){
						ReturnTransitEntity transit=PlaneInfoEntityBuilder.buildReturnTransitEntity(operatingCarrier.getString(i)+flightNumber.getString(i),
								null, airlineCodes.getString(i), "Aeroflot-Russian Airlines", "Aeroflot-Russian Airlines", departureCodes.getString(i),
								null, arrivalCodes.getString(i), null, aircraftType.getString(i));
						transit.setCabinName(classOfService.getString(i));
						JSONObject t=segments.getJSONObject(i);
						transit.setStartTime(this.parse(t.getString("departureDate"),"yyyy/MM/dd HH:mm:ss"));
						transit.setEndTime(this.parse(t.getString("arrivalDate"),"yyyy/MM/dd HH:mm:ss"));
						transit.setStayTime(t.getLong("flightDuration"));
						transits.add(transit);
						t=null;
					}
					planeInfo.setReturnTransits(transits);
				}
				
				//舱位集合
				Set<ReturnCabinEntity> returnCabins = new HashSet<ReturnCabinEntity>();
				JSONObject basketsRef=flight.getJSONObject("basketsRef");
				Iterator it = basketsRef.keys();  
				 while (it.hasNext()) {  
					 String key = (String) it.next();
					 JSONObject jcabin=basketsRef.getJSONObject(key);
					 JSONObject seatsRemaining=jcabin.getJSONObject("seatsRemaining");
					 JSONObject prices=jcabin.getJSONObject("prices");
					 JSONArray priceAlternatives=prices.getJSONArray("priceAlternatives");
					 String sumprice=priceAlternatives.getJSONObject(0).getJSONObject("pricesPerCurrency").getJSONObject("CNY").getString("amount");
					 JSONArray	moneyElements=prices.getJSONArray("moneyElements");
					 String price=null;
					 Double taxesPrice=0d;
					 for(int i=0;i<moneyElements.size();i++){
						 JSONObject moneyTO=moneyElements.getJSONObject(i).getJSONObject("moneyTO");
						 if("TAX".equalsIgnoreCase(moneyElements.getJSONObject(i).getString("type"))){
							 taxesPrice+=moneyTO.getDouble("amount");
						 }else if("FARE".equalsIgnoreCase(moneyElements.getJSONObject(i).getString("type"))){
							 price=moneyTO.getString("amount");
						 }
					 }
					 ReturnCabinEntity  cabin=PlaneInfoEntityBuilder.buildCabinInfo(CABIN.get(key), seatsRemaining.getString("customLabelSuffix"), CABIN.get(key), 
							 taxesPrice.toString(),price, sumprice, 
							 null, null, ReturnCabinEntity.class);
					 returnCabins.add(cabin);
				 }
				 planeInfo.setReturnCabins(returnCabins);
				 returnPlaneInfos.add(planeInfo);
			} catch (Exception e) {
				logger.error(String.format("[%s]解析[%s] [%s]-[%s] [%s]-[%s]  错误[%s]", super.taskQueue.getGrabChannel(), super.getRouteType().getName(), taskQueue.getFromCity(), taskQueue.getToCity(), taskQueue.getFlightDate(),
						taskQueue.getReturnGrabDate(), e));
			}
			
		}
		return  returnPlaneInfos;
	}


	/**
	 * 解析去程
	 * @param goWay
	 * @return
	 */
	private  List<DoublePlaneInfoEntity> paraseList(JSONArray goWay){
		List<DoublePlaneInfoEntity> result=new ArrayList<DoublePlaneInfoEntity>();
		for(Object obj:goWay){
			try {
				JSONObject flight=(JSONObject) obj;
				JSONObject trip=flight.getJSONObject("itineraryPartData");
				JSONArray flightNumber=trip.getJSONArray("flightNumber");//航班号 2314
				JSONArray departureCodes=trip.getJSONArray("departureCodes");//出发城市三字码
				JSONArray arrivalCodes=trip.getJSONArray("arrivalCodes");//到达城市三字码
				JSONArray aircraftType=trip.getJSONArray("aircraftType");//飞机类型
				JSONArray classOfService= trip.getJSONArray("classOfService");//舱位名称
				JSONArray operatingCarrier=trip.getJSONArray("operatingCarrier");//简称  //SU
				JSONArray airlineCodes=trip.getJSONArray("airlineCodes");//航空公司简称
				//TODO 运营商抓不到…… 只能抓到简称
				DoublePlaneInfoEntity planeInfo=PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, airlineCodes.getString(0),
						"Aeroflot-Russian Airlines", "Aeroflot-Russian Airlines",
						null, null, 
						operatingCarrier.getString(0)+flightNumber.getString(0),
						null, null, null, aircraftType.getString(0), null, null, null, null,
						DoublePlaneInfoEntity.class);
				planeInfo.setFlightDuration(trip.getLong("totalTripDuration"));//飞行总时长
				planeInfo.setStartTime(this.parse(trip.getString("departureDate"),"yyyy/MM/dd HH:mm:ss"));
				planeInfo.setEndTime(this.parse(trip.getString("arrivalDate"),"yyyy/MM/dd HH:mm:ss"));
				planeInfo.setCurrency("CNY");
				
				//中转集合
				if(flightNumber.size()>1){
					JSONArray segments= flight.getJSONArray("segments"); //中转
					ArrayList<TransitEntity> transits = new ArrayList<TransitEntity>();
					for(int i=0;i<flightNumber.size();i++){
						TransitEntity transit=PlaneInfoEntityBuilder.buildTransitEntity(operatingCarrier.getString(i)+flightNumber.getString(i),
								null, airlineCodes.getString(i), "Aeroflot-Russian Airlines", "Aeroflot-Russian Airlines", departureCodes.getString(i),
								null, arrivalCodes.getString(i), null, aircraftType.getString(i));
						transit.setCabinName(classOfService.getString(i));
						JSONObject t=segments.getJSONObject(i);
						transit.setStartTime(this.parse(t.getString("departureDate"),"yyyy/MM/dd HH:mm:ss"));
						transit.setEndTime(this.parse(t.getString("arrivalDate"),"yyyy/MM/dd HH:mm:ss"));
						transit.setStayTime(t.getLong("flightDuration"));
						transits.add(transit);
						t=null;
					}
					planeInfo.setTransits(transits);
				}
				
				//舱位集合
				Set<CabinEntity> cabins = new HashSet<CabinEntity>();
				JSONObject basketsRef=flight.getJSONObject("basketsRef");
				Iterator it = basketsRef.keys();  
				 while (it.hasNext()) {  
					 String key = (String) it.next();
					 JSONObject jcabin=basketsRef.getJSONObject(key);
					 JSONObject seatsRemaining=jcabin.getJSONObject("seatsRemaining");
					 JSONObject prices=jcabin.getJSONObject("prices");
					 JSONArray priceAlternatives=prices.getJSONArray("priceAlternatives");
					 String sumprice=priceAlternatives.getJSONObject(0).getJSONObject("pricesPerCurrency").getJSONObject("CNY").getString("amount");
					 JSONArray	moneyElements=prices.getJSONArray("moneyElements");
					 String price=null;
					 Double taxesPrice=0d;
					 for(int i=0;i<moneyElements.size();i++){
						 if("TAX".equalsIgnoreCase(moneyElements.getJSONObject(i).getString("type"))){
							 taxesPrice+=moneyElements.getJSONObject(i).getJSONObject("moneyTO").getDouble("amount");
						 }else if("FARE".equalsIgnoreCase(moneyElements.getJSONObject(i).getString("type"))){
							 price=moneyElements.getJSONObject(i).getJSONObject("moneyTO").getString("amount");
						 }
					 }
					

					 CabinEntity  cabin=PlaneInfoEntityBuilder.buildCabinInfo(CABIN.get(key), seatsRemaining.getString("customLabelSuffix"), CABIN.get(key), 
							 taxesPrice.toString(),price, sumprice, 
							 null, null, CabinEntity.class);
					 cabins.add(cabin);
				 }
				 planeInfo.setCabins(cabins);
				 result.add(planeInfo);
			} catch (Exception e) {
				logger.error(String.format("[%s]解析[%s] [%s]-[%s] [%s]-[%s]  错误[%s]", super.taskQueue.getGrabChannel(), super.getRouteType().getName(), taskQueue.getFromCity(), taskQueue.getToCity(), taskQueue.getFlightDate(),
						taskQueue.getReturnGrabDate(), e));
			}
			
		}
		return  result;
	}


//	/**
//	 * 测试的时候临时从文件读取网页。
//	 * @return
//	 * @throws IOException 
//	 */
//	
//	private  String readFile()  
//	{     
//	    StringBuffer fileContent = new StringBuffer();      
//	    try   
//	    {       
//	        File f = new File("D:\\temp\\aero.html");      
//	        if(f.isFile()&&f.exists())  
//	        {       
//	            InputStreamReader read = new InputStreamReader(new FileInputStream(f),"UTF-8");       
//	            BufferedReader reader=new BufferedReader(read);       
//	            String line;       
//	            while ((line = reader.readLine()) != null)   
//	            {        
//	                fileContent.append(line);    
//	            }         
//	            read.close();      
//	        }     
//	    } catch (Exception e)   
//	    {         
//	        e.printStackTrace();     
//	    }     
//	    return fileContent.toString();   
//	}

	/**
	 * 抓取第一步
	 * @param cabinType
	 * @return
	 * @throws Exception 
	 */
	private String fetchStep(String cabinType) throws Exception {
		
		// https://reservation.aeroflot.ru/SSW2010/7B47/webqtrip.html?searchType=NORMAL
		//&journeySpan=RT&alternativeLandingPage=1&numAdults=1&numChildren=0&numInfants=0
		//&numYouth=0&lang=en&currency=CNY&cabinClass=ECONOMY&referrerCode=AFLBOOK
		//&utm_source=(direct)&utm_campaign=(direct)&utm_medium=(none)&utm_content=&utm_term
		//=&origin=PEK&destination=LON&departureDate=2014-09-13&returnDate=2014-09-20			
		HttpClient httpclient=null;
		String url="https://reservation.aeroflot.ru/SSW2010/7B47/webqtrip.html";
		String page = null;
		Map<String, String> params = Maps.newHashMap();
		try{
			params.put("alternativeLandingPage","1");
			params.put("cabinClass",cabinType);
			params.put("currency", "CNY");
			params.put("journeySpan", taskQueue.getIsReturn() == 1 ? "RT" : "OW");
			params.put("lang", "en");
			params.put("numAdults", "1");
			params.put("numChildren", "0");
			params.put("numInfants", "0");
			params.put("numYouth", 0+"");
			params.put("referrerCode", "AFLBOOK");
			params.put("searchType", "NORMAL");
			params.put("utm_campaign", "(direct)");
			params.put("utm_content", "");
			params.put("utm_medium", "(none)");
			params.put("utm_source", "(direct)");
			params.put("utm_term", "");
			params.put("origin", taskQueue.getFromCity());
			params.put("destination", taskQueue.getToCity());
			params.put("departureDate", taskQueue.getFlightDate());
			params.put("returnDate", taskQueue.getReturnGrabDate());
			
			HttpPost httpPost=super.getBasePost(url, params);
			httpPost.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
			httpPost.setHeader("Accept-Encoding", "gzip, deflate");
			httpPost.setHeader("Accept-Language", "zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3");
			httpPost.setHeader("Connection", "keep-alive");
			httpPost.setHeader("DNT", "1");
			httpPost.setHeader("Host", "reservation.aeroflot.ru");
			httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0");
			
			httpclient=getHttpClient();
			
			setContext();
			
			HttpResponse response=httpclient.execute(httpPost, context);
			
			checkStatusCode(response.getStatusLine().getStatusCode());
			
				
			if(302==response.getStatusLine().getStatusCode()){
		    	  Header location= response.getFirstHeader("Location");
		    	  String newuri = location.getValue();
    			  httpPost.releaseConnection();
		    	  HttpPost newPost= new HttpPost(newuri);
		    	  response=httpclient.execute(newPost, context);
		    	  HttpEntity entity = response.getEntity();
		    	  if (entity != null) {
		    		page = EntityUtils.toString(entity);
		    		super.appendPageContents(page);
		    		Document doc = Jsoup.parse(page);
		    		Elements inputs=doc.select("input[type=radio][name=sel_outbounds]");
		    		if(null==inputs || inputs.size()==0)
		    			return "Not FlightNo";
		      		Element input = inputs.get(0);
		      		System.out.println(input.attr("id"));
		      		String id=input.attr("id").split("_")[2];
		      		EntityUtils.consumeQuietly(entity);
			    	newPost.releaseConnection();
		      		page=null;
		      		String jsonUrl="https://reservation.aeroflot.ru/SSW2010/7B47/webqtrip.html";
		      		Map<String, String> jsonparams = Maps.newHashMap();
		      		jsonparams.put("_eventId_ajax", "");
		      		jsonparams.put("ajaxSource", "true");
		      		jsonparams.put("execution", "e1s1");
		      		jsonparams.put("contextObject", "{\"transferObjects\":[{\"componentType\":\"flc\",\"actionCode\"" +
		      				":\"getAdvisors\",\"queryData\":{\"componentId\":\"flc_1\",\"componentType\":\"flc\"," +
		      				"\"actionCode\":\"getAdvisors\",\"queryData\":null,\"basketHashRefs\":["+id +
		      				"],\"direction\":\"outbounds\",\"requestPartials\":[\"__flightAdvisorOutboundRow\"]}}]}");
		      		HttpPost post=super.getBasePost(jsonUrl, jsonparams);
		      		response=httpclient.execute(post, context);
		      		checkStatusCode(response.getStatusLine().getStatusCode());
		      		entity=response.getEntity();
		      		page = EntityUtils.toString(entity);
		      		EntityUtils.consumeQuietly(entity);
		      		post.releaseConnection();
		    	  }
		    	  entity=null;
		      }
			super.appendPageContents(page);
		//	super.setLenghtCount(page.getBytes().length);
		} catch (Exception e) {
			rollbackProxyIp(false);
			logger.info("抓取航班数据失败：" + e.getMessage());
			throw e;
		} finally {
			params = null;
		}
		return page; 
	}
	
	
	
	private Date parse(String source, String pattern) {
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		try {
			return sdf.parse(source);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	  private void setContext() {
		   context=HttpClientContext.create();
		    Registry<CookieSpecProvider> registry = RegistryBuilder
		        .<CookieSpecProvider> create()
		        .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
		        .register(CookieSpecs.BROWSER_COMPATIBILITY,
		            new BrowserCompatSpecFactory()).build();
		    context.setCookieSpecRegistry(registry);
		    context.setCookieStore( new BasicCookieStore());
		  }
	  
	  private void checkStatusCode(int statuscode) throws Exception{
		  if(statuscode>=400){
			  switch (statuscode) {
			case 400: throw new PageErrorResultException(statuscode + "400  错误请求 ");
			case 401: throw new PageErrorResultException(statuscode + "401  未授权 ，访问不到服务器资源");
			case 402: throw new PageErrorResultException(statuscode + "402  需要付款  ");
			case 403: throw new PageErrorResultException(statuscode + "403  服务器禁止访问  ");
			case 404: throw new PageErrorResultException(statuscode + "404  找不到 ");

			default:
				throw new Exception(statuscode + "");
			}
		  }
		  
	  }
}
